﻿/*** CharacterScanner Class - FlashFurc Core
 * ---
 * Author: IceDragon <icedragon@quickfox.org>
 * Web:    www.icerealm.org
 * ---
 * 
 * Copyright (c) 2010, IceRealm.org
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   # Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   # Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   # Neither the name of the IceRealm.org nor the names of its contributors
 *     may be used to endorse or promote products derived from this software
 *     without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

using System.Collections.Generic;
using System.IO;

using Furcadia;

namespace FlashFurc
{
    /// <summary>
    /// This class is responsible for detection and identification of Furcadia
    /// character files: Check what characters the user has and where are the
    /// INI files belonging to them for copying.
    /// </summary>
    public class CharacterScanner
    {
        /// <summary>
        /// Scan all the relevant folders for Furcadia characters and return
        /// back their names and paths to their INI files.
        /// </summary>
        /// <returns>A dictionary with the INI path as a key and character name as the value</returns>
        public static Dictionary<FileInfo, string> GetCharacters()
        {
            Dictionary<FileInfo,string> result = new Dictionary<FileInfo,string>();
            string path;

            // Try looking in the user's character folder
            //   (%USERPROFILE%\Documents\Furcadia\Furcadia Characters\)
            path = Paths.CharacterPath;
            Merge(ref result, GetCharacters(path));

            // Try looking in the Furcadia intsall path for any character files
            //   (c:\Program Files\Furcadia\)
            path = Paths.GetFurcadiaInstallPath();
            if (path != null)
                Merge(ref result, GetCharacters(path));

            // Return whatever we have gathered.
            return result;
        }

        /// <summary>
        /// Scan a specific folder for Furcadia characters and return back
        /// their names and paths to their INI files.
        /// 
        /// NOTE: This function will not recurse into sub-directories if there
        ///       are any!
        /// </summary>
        /// <param name="path">A path to scan for INI files</param>
        /// <returns>A dictionary with the INI path as a key and character name as the value</returns>
        public static Dictionary<FileInfo, string> GetCharacters(string path)
        {
            Dictionary<FileInfo, string> result = new Dictionary<FileInfo, string>();

            if (Directory.Exists(path))
            {
                DirectoryInfo di = new DirectoryInfo(path);
                foreach (FileInfo fi in di.GetFiles("*.ini"))
                {
                    string name = GetCharacterName(fi);
                    if (name != null)
                        result[fi] = name;
                }
            }

            return result;
        }
        
        /// <summary>
        /// Check if a given file if a valid Furcadia character file.
        /// </summary>
        /// <param name="fi">Info of the file to look at</param>
        /// <returns>TRUE if it's a valid character file, FALSE otherwise.</returns>
        public static bool IsCharacterFile(FileInfo fi)
        {
            using (StreamReader sr = new StreamReader(fi.OpenRead()))
            {
                return CheckHeaderLine(sr.ReadLine());
            }
        }


        #region Private Functions
        /// <summary>
        /// Verify if the line given is a valid character file header.
        /// </summary>
        /// <param name="line">Header line (i.e. "V3.0 character")</param>
        /// <returns>TRUE if it's a valid Furcadia character header. FALSE if it isn't.</returns>
        private static bool CheckHeaderLine(string line)
        {
            try
            {
                return line.Substring(5, 9) == "character";
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Read a character file, verify it and extract the character name
        /// from within it. The resulting name will have pipes (|) replaced
        /// with spaces.
        /// </summary>
        /// <param name="file">Path to the INI file to scan</param>
        /// <returns>Character name that belongs to the INI file or null upon failure</returns>
        private static string GetCharacterName(FileInfo file)
        {
            try
            {
                StreamReader sr = new StreamReader(file.OpenRead());
                string line = sr.ReadLine();
                string name = null;

                // Check if the version string is familiar.
                if (CheckHeaderLine(line))
                {
                    // Find and extract the character name.
                    do
                    {
                        line = sr.ReadLine();
                        if (line != null && line.StartsWith("Name="))
                        {
                            name = line.Substring(5);
                            name = name.Trim().Replace('|', ' ');
                        }
                    } while (!sr.EndOfStream || name == null);
                }

                sr.Close();
                return name;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Merge the source dictionary into the target dictionary, overwriting
        /// any similar keys.
        /// </summary>
        /// <param name="target">Dictionary to merge into</param>
        /// <param name="source">Dictionary to merge from</param>
        private static void Merge(ref Dictionary<FileInfo, string> target, Dictionary<FileInfo, string> source)
        {
            foreach (FileInfo fi in source.Keys)
                target[fi] = source[fi];
        }
        #endregion
    }
}
